using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using RAGApp.Api.Configurations;
using RAGApp.Api.Data;
using RAGApp.Api.Models;
using RAGApp.Api.Models.DTOs;
using RAGApp.Api.Services.Interfaces;
using System.Security.Cryptography;
using System.Net.WebSockets;

namespace RAGApp.Api.Services;

public class XunfeiChatService : IChatService
{
    private readonly ApplicationDbContext _context;
    private readonly XunfeiConfig _config;
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IEmbeddingService _embeddingService;
    private readonly ILogger<XunfeiChatService> _logger;

    public XunfeiChatService(
        ApplicationDbContext context,
        IOptions<XunfeiConfig> config,
        IHttpClientFactory httpClientFactory,
        IEmbeddingService embeddingService,
        ILogger<XunfeiChatService> logger)
    {
        _context = context;
        _config = config.Value;
        _httpClientFactory = httpClientFactory;
        _embeddingService = embeddingService;
        _logger = logger;
    }

    public async Task<ChatResponseDto> GetAnswerAsync(ChatRequestDto request)
    {
        // 获取或创建对话
        var conversationId = request.ConversationId ?? Guid.NewGuid();
        var conversation = await _context.Conversations
            .Include(c => c.Messages)
            .FirstOrDefaultAsync(c => c.Id == conversationId);

        if (conversation == null)
        {
            conversation = await CreateConversationAsync();
            conversationId = conversation.Id;
        }

        // 获取相关文档块
        var questionEmbedding = await _embeddingService.GenerateEmbeddingAsync(request.Question, isQuery: true);

        // 计算余弦相似度并获取最相关的文档块
        var chunks = await _context.DocumentChunks
            .Include(c => c.Document)
            .ToListAsync();

        var relevantChunks = chunks
            .Select(chunk => new
            {
                Chunk = chunk,
                Similarity = CosineSimilarity(chunk.Embedding?.ToArray() ?? Array.Empty<float>(), questionEmbedding)
            })
            .OrderByDescending(x => x.Similarity)
            .Take(3)
            .Select(x => x.Chunk)
            .ToList();

        // 构建提示词
        var prompt = BuildPrompt(request.Question, relevantChunks);

        // 获取历史对话
        var history = conversation.Messages
            .OrderBy(m => m.CreatedAt)
            .Select(m => new { role = m.Role, content = m.Content })
            .ToList();

        // 调用讯飞API获取回答
        var response = await GetChatResponseAsync(prompt, history.Cast<object>().ToList());

        // 保存对话记录
        var userMessage = new Message
        {
            Id = Guid.NewGuid(),
            ConversationId = conversationId,
            Role = "user",
            Content = request.Question,
            CreatedAt = DateTime.UtcNow
        };

        var assistantMessage = new Message
        {
            Id = Guid.NewGuid(),
            ConversationId = conversationId,
            Role = "assistant",
            Content = response,
            CreatedAt = DateTime.UtcNow
        };

        _context.Messages.AddRange(userMessage, assistantMessage);
        await _context.SaveChangesAsync();

        // 返回结果
        return new ChatResponseDto
        {
            Answer = response,
            ConversationId = conversationId,
            Sources = relevantChunks.Select(chunk => new SourceDto
            {
                Content = chunk.Content,
                Source = chunk.Document.Source,
                Similarity = CosineSimilarity(chunk.Embedding?.ToArray() ?? Array.Empty<float>(), questionEmbedding)
            }).ToList()
        };
    }

    private double CosineSimilarity(float[] vectorA, float[] vectorB)
    {
        if (vectorA.Length != vectorB.Length)
            return 0;

        double dotProduct = 0.0;
        double normA = 0.0;
        double normB = 0.0;

        for (int i = 0; i < vectorA.Length; i++)
        {
            dotProduct += vectorA[i] * vectorB[i];
            normA += vectorA[i] * vectorA[i];
            normB += vectorB[i] * vectorB[i];
        }

        if (normA == 0 || normB == 0)
            return 0;

        return dotProduct / (Math.Sqrt(normA) * Math.Sqrt(normB));
    }

    public async Task<List<Message>> GetConversationHistoryAsync(Guid conversationId)
    {
        return await _context.Messages
            .Where(m => m.ConversationId == conversationId)
            .OrderBy(m => m.CreatedAt)
            .ToListAsync();
    }

    public async Task<Conversation> CreateConversationAsync()
    {
        var conversation = new Conversation
        {
            Id = Guid.NewGuid(),
            UserId = Guid.NewGuid(), // 临时用户ID，后续可以改为实际用户
            CreatedAt = DateTime.UtcNow,
            UpdatedAt = DateTime.UtcNow
        };

        _context.Conversations.Add(conversation);
        await _context.SaveChangesAsync();

        return conversation;
    }

    private string BuildPrompt(string question, List<DocumentChunk> relevantChunks)
    {
        var sb = new StringBuilder();
        sb.AppendLine("基于以下参考信息回答问题。如果参考信息不足以回答问题，请说明无法回答。");
        sb.AppendLine("\n参考信息：");
        
        foreach (var chunk in relevantChunks)
        {
            sb.AppendLine($"- {chunk.Content}");
        }

        sb.AppendLine("\n问题：");
        sb.AppendLine(question);
        
        return sb.ToString();
    }

    private async Task<string> GetChatResponseAsync(string prompt, List<dynamic> history)
    {
        try
        {
            var date = DateTime.UtcNow.ToString("r");
            var host = _config.ApiDomain;
            var path = _config.ChatPath;

            _logger.LogInformation($"准备发送聊天请求:");
            _logger.LogInformation($"Host: {host}");
            _logger.LogInformation($"Path: {path}");
            _logger.LogInformation($"Date: {date}");
            _logger.LogInformation($"Domain: {_config.Domain}");

            // 构造消息列表
            var messages = new List<object>();
            
            // 添加历史消息
            foreach (var msg in history)
            {
                messages.Add(new
                {
                    role = msg.role.ToString().ToLower(),
                    content = msg.content.ToString()
                });
            }
            
            // 添加当前问题
            messages.Add(new
            {
                role = "user",
                content = prompt
            });

            // 构造请求体
            var chatId = Guid.NewGuid().ToString();
            var chatRequest = new
            {
                header = new
                {
                    app_id = _config.AppId
                },
                parameter = new
                {
                    chat = new
                    {
                        domain = _config.Domain ?? "4.0Ultra", // 防止为null
                        temperature = 0.5,
                        max_tokens = 1024,
                        top_k = 4,
                        chat_id = chatId
                    }
                },
                payload = new
                {
                    message = new
                    {
                        text = messages.Select(m => new
                        {
                            role = ((dynamic)m).role.ToString().ToLower(),
                            content = ((dynamic)m).content.ToString()
                        }).ToArray()
                    }
                }
            };

            var jsonBody = JsonSerializer.Serialize(chatRequest, new JsonSerializerOptions 
            { 
                WriteIndented = false,
                PropertyNamingPolicy = null  // 使用原始属性名，不进行驼峰转换
            });
            _logger.LogInformation($"请求体: {jsonBody}");

            // 判断domain，决定wsVersion和path
            string wsVersion = "";
            string realPath = path;
            if ((_config.Domain != null && _config.Domain.Contains("v4.0")) || (_config.Domain != null && _config.Domain.ToLower().Contains("ultra")))
            {
                wsVersion = "v4.0";
                realPath = "/v4.0/chat";
            }
            else
            {
                realPath = path;
            }

            // 构建签名字符串
            var requestLine = $"GET {realPath} HTTP/1.1";
            var stringToSign = string.Format("host: {0}\ndate: {1}\n{2}", host, date, requestLine);
            _logger.LogInformation($"待签名字符串:\n{stringToSign}");

            // 计算签名
            using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_config.ApiSecret));
            var signatureBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
            var signature = Convert.ToBase64String(signatureBytes);

            // 构造 authorization
            var authorizationOrigin = string.Format(
                "api_key=\"{0}\", algorithm=\"{1}\", headers=\"{2}\", signature=\"{3}\"",
                _config.ApiKey,
                "hmac-sha256",
                "host date request-line",
                signature);

            var authorization = Convert.ToBase64String(Encoding.UTF8.GetBytes(authorizationOrigin));

            // 构造 WebSocket URL
            var queryParams = new Dictionary<string, string>
            {
                { "host", host },
                { "date", date },
                { "authorization", authorization }
            };
            var queryString = string.Join("&", queryParams.Select(kv => $"{kv.Key}={Uri.EscapeDataString(kv.Value)}"));
            
            string wsUrl;
            if (!string.IsNullOrEmpty(wsVersion))
            {
                wsUrl = $"wss://spark-api.xf-yun.com/{wsVersion}/chat?{queryString}";
            }
            else
            {
                wsUrl = $"wss://{host}{path}?{queryString}";
            }

            _logger.LogInformation($"WebSocket URL: {wsUrl}");

            using var ws = new ClientWebSocket();
            // 添加必要的头部
            ws.Options.SetRequestHeader("Host", host);
            ws.Options.SetRequestHeader("Date", date);
            ws.Options.SetRequestHeader("Authorization", authorization);

            await ws.ConnectAsync(new Uri(wsUrl), CancellationToken.None);

            // 发送消息
            var messageBytes = Encoding.UTF8.GetBytes(jsonBody);
            await ws.SendAsync(new ArraySegment<byte>(messageBytes), WebSocketMessageType.Text, true, CancellationToken.None);

            // 接收响应
            var buffer = new byte[4096];
            var result = new StringBuilder();

            while (true)
            {
                var receiveResult = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                var response = Encoding.UTF8.GetString(buffer, 0, receiveResult.Count);
                _logger.LogInformation($"WebSocket响应: {response}");

                var responseObj = JsonSerializer.Deserialize<JsonElement>(response);
                var header = responseObj.GetProperty("header");
                var code = header.GetProperty("code").GetInt32();

                if (code != 0)
                {
                    var message = header.GetProperty("message").GetString();
                    throw new Exception($"讯飞API返回错误：{message}");
                }

                var payload = responseObj.GetProperty("payload");
                var choices = payload.GetProperty("choices");
                var textArray = choices.GetProperty("text");
                string content = "";
                foreach (var item in textArray.EnumerateArray())
                {
                    if (item.TryGetProperty("content", out var c))
                        content += c.GetString();
                }
                var status = choices.GetProperty("status").GetInt32();

                result.Append(content);

                if (status == 2)
                {
                    break;
                }
            }

            await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Completed", CancellationToken.None);
            return result.ToString();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "调用讯飞聊天API时发生错误");
            throw;
        }
    }
} 